"""
    Copyright 2011 Kacper Żuk <kazuldur@gmail.com>

    This file is part of pyLastPlayer.

    pyLastPlayer is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    pyLastPlayer is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with pyLastPlayer.  If not, see <http://www.gnu.org/licenses/>.
"""


from urllib.request import urlopen
from urllib.error import HTTPError
import os, shutil
import logging
from PyQt4.QtCore import QThread, QSettings, QFile, SIGNAL, QObject
from LastLib import LastClient, services

try:
    import stagger
    taglib = True
    
    # Workaround problem with threads
    from contextlib import contextmanager
    @contextmanager
    def hack():
        yield None
    stagger.fileutil.suppress_interrupt = hack
except ImportError:
    taglib = False


def _guessGenre(artist, service):
    l = LastClient(service)
    e, tags = l.getTopTags(artist)
    if e != 0 or not tags:
        return None
    for tag in tags:
        if tag in stagger.id3.genres:
            return tag

def guessGenre(artist):
    for s in range(len(services)):
        g = _guessGenre(artist, s)
        if g:
            return g

class Worker(QThread):
    def __init__(self):
        QThread.__init__(self)
        self.log = logging.getLogger("Downloader.Worker")
        self.die = False
        self.saveFlag = False

    def quit(self):
        self.die = True

    def save(self):
        if self.isFinished():
            self._save()
        else:
            self.saveFlag = True

    def run(self):
        t = self.track
        ##### TRACK DOWNLOAD #####
        try:
            src = urlopen(t.location)
            lenght = int(src.info()['Content-Length'])
            dest = QFile(t.file)
            dest.open(QFile.WriteOnly | QFile.Truncate | QFile.Unbuffered)
            dest.write(b"\0"*lenght)
            dest.seek(0)
            data = src.read(2048)
            dest.write(data)
            self.emit(SIGNAL("started(QString)"), t.file)
            data = src.read(1024)
            while data and not self.die:
                dest.write(data)
                data = src.read(1024)
            dest.close()
        except HTTPError as e:
            self.log.warning("HTTPError for location %s (%d): %s" % (t.location, e.code, e.read()))
            self.die = True
            
        #### SAVE ####
        if self.saveFlag and not self.die:
            self._save()

    def _save(self):
        t = self.track
        s = QSettings()
        if not s.contains("settings/directory"):
            return
        directory = s.value("settings/directory")
        if t.album:
            path = os.path.join(directory, t.artist, t.album, t.title+".mp3")
        else:
            path = os.path.join(directory, t.artist, t.title+".mp3")

        if os.path.exists(path):
            self.log.info("Already exists: %s" % path)
            return
        if not os.path.isdir(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        shutil.copyfile(t.file, path)
        coverpath = os.path.join(os.path.dirname(path), "cover.jpg")
        if not os.path.exists(coverpath) and t.image and not self.die:
            try:
                src = urlopen(t.image)
                dest = QFile(coverpath)
                dest.open(QFile.WriteOnly)
                data = src.read(1024)
                while data and not self.die:
                    dest.write(data)
                    data = src.read(1024)
                dest.close()
            except HTTPError:
                self.log.warning("HTTPError for image %s (%d): %s" (t.image, e.code, e.read()))

        if taglib and not self.die:
            try:
                tag = stagger.read_tag(path)
            except:
                tag = stagger.Tag23()
            tag.artist = t.artist
            if t.album:
                tag.album = t.album
            tag.title = t.title
            if os.path.exists(coverpath):
                tag.picture = coverpath
            if s.value("settings/guessGenre") == "true":
                g = guessGenre(t.artist)
                if g:
                    tag.genre = g
            tag.write(path)

        if self.die:
            try:
                os.unlink(path)
                path = os.path.dirname(path)
                while not path == directory:
                    os.rmdir(path)
                    path = os.path.dirname(path)
            except OSError:
                pass
        else:
            self.emit(SIGNAL("done(QString)"), path)

class Downloader(QObject):
    def __init__(self):
        QObject.__init__(self)
        self.log = logging.getLogger("Downloader")
        if taglib:
            self.log.info("Tagging enabled")
        else:
            self.log.info("Missing stagger, tagging disabled")
        self.threads = dict()

    def done(self, path):
        self.emit(SIGNAL("updateStatus(QString)"), self.tr("Saved %s..." % path))

    def started(self, file):
        self.emit(SIGNAL("started(QString)"), file)

    def quit(self):
        for f, t in self.threads.items():
            t.quit()
            try:
                os.unlink(f)
            except:
                pass
            self.log.debug("Deleted %s" % f)

    def download(self, track):
        self.threads[track.file] = Worker()
        self.threads[track.file].track = track
        self.connect(self.threads[track.file], SIGNAL("done(QString)"), self.done)
        self.connect(self.threads[track.file], SIGNAL("started(QString)"), self.started)
        self.threads[track.file].start()

    def delete(self, file):
        try:
            os.unlink(file)
            self.log.debug("Deleted %s" % file)
        except:
            pass
        try:
            del self.threads[file]
        except:
            pass

    def save(self, track):
        self.threads[track.file].save()
